//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  EtwNativeMethods.cs
//    
// Abstract:
//
//  This module defines the native methods used by the EtwTraceController and EtwTraceConsumer classes.
//  
//--

using System;
using System.Runtime.InteropServices;

namespace Microsoft.OfficeCommunicationsServer.Applications.Common.UnitTests
{
    #region internal event trace enumerations

    /// <summary>
    /// Indicates which structure members are valid:
    /// </summary>
    [Flags]
    internal enum EventTraceFlags : ushort
    {
        /// <summary>
        /// EVENT_TRACE_USE_PROCTIME - ProcessorTime is valid. 
        /// </summary>
        ProcessorTimeValid = 0x01,
        /// <summary>
        /// EVENT_TRACE_USE_NOCPUTIME - KernelTime, UserTime, and ProcessorTime are not used. 
        /// </summary>
        NoTimes = 0x02,
    }

    /// <summary>
    /// Type of event. An event type can be user-defined or predefined. 
    /// This enum identifies the general predefined event types.
    /// </summary>
    internal enum EventTraceType : byte
    {
        /// <summary>
        /// EVENT_TRACE_TYPE_INFO - Informational event. This is the default event type. 
        /// </summary>
        Info = 0x00,
        /// <summary>
        /// EVENT_TRACE_TYPE_START - Start event. Use to trace the initial state of a multi-step event.
        /// </summary>
        Start = 0x01,
        /// <summary>
        /// EVENT_TRACE_TYPE_END - End event. Use to trace the final state of a multi-step event. 
        /// </summary>
        End = 0x02,
        /// <summary>
        /// EVENT_TRACE_TYPE_DC_START - Collection start event. 
        /// </summary>
        CollectionStart = 0x03,
        /// <summary>
        /// EVENT_TRACE_TYPE_DC_END - Collection end event. 
        /// </summary>
        CollectionEnd = 0x04,
        /// <summary>
        /// EVENT_TRACE_TYPE_EXTENSION - Extension event. Use for an event that is a continuation of 
        /// a previous event. For example, use the extension event type when an event trace 
        /// records more data than can fit in a session buffer. 
        /// </summary>
        Extension = 0x05,
        /// <summary>
        /// EVENT_TRACE_TYPE_REPLY - Reply event. Use when an application that requests resources c
        /// an receive multiple responses. For example, if a client application requests a URL, 
        /// and the Web server reply is to send several files, each file received can be marked as a reply event. 
        /// </summary>
        Reply = 0x06,
        /// <summary>
        /// EVENT_TRACE_TYPE_DEQUEUE - Dequeue event. Use when an activity is queued before it begins.
        /// Use EVENT_TRACE_TYPE_START to mark the time when a work item is queued. Use the dequeue 
        /// event type to mark the time when work on the item actually begins. Use EVENT_TRACE_TYPE_END 
        /// to mark the time when work on the item completes. 
        /// </summary>
        Dequeue = 0x07,
        /// <summary>
        /// EVENT_TRACE_TYPE_CHECKPOINT - Checkpoint event. Use for an event that is not at the start or end of an activity. 
        /// </summary>
        Checkpoint = 0x08,
    }

    #endregion

    /// <summary>
    /// An internal class that contains all of the marshaling and structure definitions for native API calls.
    /// </summary>
    internal static class NativeMethods
    {
        // Win32 Error Codes
        internal const int ERROR_SUCCESS = 0;
        internal const int ERROR_CANCELLED = 1223;
        internal const int ERROR_WMI_INSTANCE_NOT_FOUND = 4201;

        internal static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);

        internal static bool IsValidHandle(ulong handle)
        {
            IntPtr handleValue;

            unchecked
            {
                if (4 == IntPtr.Size)
                {
                    handleValue = new IntPtr((int)handle);
                }
                else
                {
                    handleValue = new IntPtr((long)handle);
                }
            }
            return INVALID_HANDLE_VALUE != handleValue;
        }

        #region Trace Controller API

        //	ULONG StartTrace(PTRACEHANDLE SessionHandle, LPCTSTR SessionName, PEVENT_TRACE_PROPERTIES Properties);
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
        internal static extern uint StartTrace([Out] out ulong traceHandle, [In] string sessionName, [In] ref EVENT_TRACE_PROPERTIES properties);

        //	ULONG StopTrace(TRACEHANDLE SessionHandle, LPCTSTR SessionName, PEVENT_TRACE_PROPERTIES Properties);
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
        internal static extern uint StopTrace([In]ulong traceHandle, [In] string sessionName, [In, Out] ref EVENT_TRACE_PROPERTIES properties);

        // ULONG EnableTrace(ULONG Enable, ULONG EnableFlag, ULONG EnableLevel, LPCGUID ControlGuid, TRACEHANDLE SessionHandle);
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
        internal static extern uint EnableTrace([In] uint enable, [In] uint enableFlag, [In] uint enableLevel, [In] ref NativeMethods.InternalGuid controlGuid, [In]ulong traceHandle);

        // ULONG FlushTrace(TRACEHANDLE SessionHandle, LPCTSTR SessionName, PEVENT_TRACE_PROPERTIES Properties);
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
        internal static extern uint FlushTrace([In]ulong traceHandle, [In] string sessionName, [In, Out] ref EVENT_TRACE_PROPERTIES properties);

        #endregion

        #region Trace Processing API

        //	TRACEHANDLE OpenTrace(PEVENT_TRACE_LOGFILE Logfile);
        [DllImport("advapi32.dll", EntryPoint = "OpenTraceW", CharSet = CharSet.Unicode, SetLastError = true)]
        internal static extern ulong OpenTrace([In] ref EVENT_TRACE_LOGFILE eventTraceLogfile);

        //	ULONG CloseTrace(TRACEHANDLE TraceHandle);
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
        internal static extern uint CloseTrace([In] ulong traceHandle);

        //	ULONG ProcessTrace(
        //	  PTRACEHANDLE HandleArray, 
        //	  ULONG HandleCount,
        //	  LPFILETIME StartTime,
        //	  LPFILETIME EndTime
        //	);
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
        internal unsafe static extern uint ProcessTrace(
            [In] ulong[] traceHandleArray,
            [In] int handleArrayLength,
            [In] ref long startFileTime,
            [In] ref long endFileTime);

        #endregion

        #region Trace Callbacks

        //	VOID WINAPI <FunctionName> (PEVENT_TRACE pEvent);
        internal delegate void EventCallback([In] ref EVENT_TRACE eventTrace);

        //	ULONG WINAPI <FunctionName> (PEVENT_TRACE_LOGFILE Logfile);
        internal delegate bool EventTraceBufferCallback([In] ref EVENT_TRACE_LOGFILE eventTraceLogfile);

        #endregion

        #region Trace Enums and Constants

        internal enum EventTraceFileMode : uint
        {
            Sequential = 0x00000001,  // log sequentially
            Circular = 0x00000002,  // log in circular manner
            RealTime = 0x00000100   // log in real time
        }

        #endregion

        #region Guid structure

        // This structure is the same as the normal guid however a similar
        // stucture is needed where all its members are public. This is because
        // in the _EVENT_TRACE_HEADER structure below, the Guid is in a union with
        // a ulong. If we allowed a type with private fields to be overlapped 
        // then it would be possible for someone to access private implementation 
        // details of the class, which is considered a "bad thing".
        // Unions are a valid managed construct, and not used just for marshalling 
        // to Win32 or COM. If the normal Guid is used, then an exception is thrown.

        [StructLayout(LayoutKind.Sequential), Serializable]
        internal struct InternalGuid
        {
            internal int _a;
            internal short _b;
            internal short _c;
            internal byte _d;
            internal byte _e;
            internal byte _f;
            internal byte _g;
            internal byte _h;
            internal byte _i;
            internal byte _j;
            internal byte _k;

            internal InternalGuid(byte[] guidBytes)
            {
                if (guidBytes == null)
                {
                    throw new ArgumentNullException("guidBytes");
                }
                if (guidBytes.Length != 16)
                {
                    throw new ArgumentException("Wrong length", "guidBytes");
                }
                _a = BitConverter.ToInt32(guidBytes, 0);
                _b = BitConverter.ToInt16(guidBytes, 4);
                _c = BitConverter.ToInt16(guidBytes, 6);
                _d = guidBytes[8];
                _e = guidBytes[9];
                _f = guidBytes[10];
                _g = guidBytes[11];
                _h = guidBytes[12];
                _i = guidBytes[13];
                _j = guidBytes[14];
                _k = guidBytes[15];
            }

            public override bool Equals(Object o)
            {
                bool isEqual = false;
                // Check that o is a Guid first
                if (o is InternalGuid)
                {
                    InternalGuid g = (InternalGuid)o;
                    // Now compare each of the elements
                    isEqual = (g._a == _a &&
                               g._b == _b &&
                               g._c == _c &&
                               g._d == _d &&
                               g._e == _e &&
                               g._f == _f &&
                               g._g == _g &&
                               g._h == _h &&
                               g._i == _i &&
                               g._j == _j &&
                               g._k == _k);
                }
                return isEqual;
            }

            public override int GetHashCode()
            {
                return _a ^ (((int)_b << 16) | (int)(ushort)_c) ^ (((int)_f << 24) | _k);
            }

            public static bool operator ==(InternalGuid a, InternalGuid b)
            {
                return a.Equals(b);
            }

            public static bool operator !=(InternalGuid a, InternalGuid b)
            {
                return !a.Equals(b);
            }
        }

        #endregion

        #region Miscellaneous structure definitions

        internal const uint EventTracePropertiesStructSize = 120;
        internal const uint EventTracePropertiesStringSize = 1024;

        [StructLayout(LayoutKind.Sequential)]
        internal struct EVENT_TRACE_PROPERTIES
        {
            internal WNODE_HEADER WNode;
            internal uint BufferSize;
            internal uint MinimumBuffers;
            internal uint MaximumBuffers;
            internal uint MaximumFileSize;
            internal EventTraceFileMode LogFileMode;
            internal uint FlushTimer;
            internal uint EnableFlags;
            internal int AgeLimit;
            internal uint NumberOfBuffers;
            internal uint FreeBuffers;
            internal uint EventsLost;
            internal uint BuffersWritten;
            internal uint LogBuffersLost;
            internal uint RealTimeBuffersLost;
            internal unsafe void* LoggerThreadId;
            internal uint LogFileNameOffset;
            internal uint LoggerNameOffset;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)EventTracePropertiesStringSize)]
            internal string LoggerName;
        }

        #endregion

        #region WNODE_HEADER structure definition

        //	typedef struct _WNODE_HEADER
        //	{
        //	ULONG BufferSize;
        //	ULONG ProviderId;
        //	union
        //	{
        //		ULONG64 HistoricalContext;
        //		struct
        //		{
        //		ULONG Version;
        //		ULONG Linkage;
        //		};
        //	};
        //	union
        //	{
        //		HANDLE KernelHandle;
        //		LARGE_INTEGER TimeStamp;
        //	};
        //	GUID Guid;
        //	ULONG ClientContext;
        //	ULONG Flags;
        //	} WNODE_HEADER, *PWNODE_HEADER;
        // size = 48
        [StructLayout(LayoutKind.Sequential)]
        internal struct WNODE_HEADER
        {
            internal uint BufferSize;
            internal uint ProviderId;
            internal ContextVersionUnion ContextVersion;
            internal KernalTimestampUnion KernalTimestamp;
            internal InternalGuid Guid;
            internal uint ClientContext;
            internal WNodeFlags Flags;
        }

        [StructLayout(LayoutKind.Explicit)]
        internal struct ContextVersionUnion
        {
            [FieldOffset(0)]
            internal ulong HistoricalContext;

            [FieldOffset(0)]
            internal uint Version;
            [FieldOffset(4)]
            internal uint Linkage;
        }

        [StructLayout(LayoutKind.Explicit)]
        internal struct KernalTimestampUnion
        {
            [FieldOffset(0)]
            internal uint CountLost;
            [FieldOffset(0)]
            internal unsafe void* KernelHandle;
            [FieldOffset(0)]
            internal long TimeStamp;
        }

        [Flags]
        internal enum WNodeFlags : uint
        {
            UseGuidPtr = 0x00080000,  // Guid is actually a pointer
            TracedGuid = 0x00020000,  // denotes a trace
            UseMofPtr = 0x00100000  // MOF data are dereferenced
        }
        #endregion

        #region EVENT_TRACE_LOGFILE structure definition

        //	typedef struct _EVENT_TRACE_LOGFILE {
        //	  LPTSTR LogFileName;
        //	  LPTSTR LoggerName;
        //	  LONGLONG CurrentTime;
        //	  ULONG BuffersRead;
        //	  ULONG LogFileMode;
        //	  EVENT_TRACE CurrentEvent;
        //	  TRACE_LOGFILE_HEADER LogfileHeader;
        //	  PEVENT_TRACE_BUFFER_CALLBACK BufferCallback;
        //	  ULONG BufferSize;
        //	  ULONG Filled;
        //	  ULONG EventsLost;
        //	  PEVENT_CALLBACK EventCallback;
        //	  ULONG IsKernelTrace;
        //	  PVOID Context;
        //	} EVENT_TRACE_LOGFILE, *PEVENT_TRACE_LOGFILE;
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct EVENT_TRACE_LOGFILE
        {
            internal string LogfileName;
            internal string LoggerName;
            internal long CurrentTime;
            internal uint BuffersRead;
            internal uint LogFileMode;
            internal EVENT_TRACE CurrentEvent;
            internal TraceLogfileHeader LogfileHeader;
            internal EventTraceBufferCallback BufferCallback;
            internal uint BufferSize;
            internal uint Filled;
            internal uint EventsLost;
            internal EventCallback EventCallback;
            internal uint IsKernelTrace;
            internal long Context;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct TraceLogfileHeader
        {
            internal uint BufferSize;
            internal VersionDetailUnion VersionDetailUnion;
            internal uint ProviderVersion;
            internal uint NumberOfProcessors;
            internal long EndTime;
            internal uint TimerResolution;
            internal uint MaximumFileSize;
            internal uint LogFileMode;
            internal uint BuffersWritten;
            internal BufferUnion BufferUnion;
            internal IntPtr LoggerName;
            internal IntPtr LogFileName;
            internal TimeZoneInformation TimeZone;
            internal long BootTime;
            internal long PerfFreq;
            internal long StartTime;
            internal uint ReservedFlags;
            internal uint BuffersLost;
        }

        //	  union {
        //		ULONG     Version;
        //		struct {
        //		  UCHAR   MajorVersion;
        //		  UCHAR   MinorVersion;
        //		  UCHAR   SubVersion;
        //		  UCHAR   SubMinorVersion;
        //		} VersionDetail;
        //	  };
        [StructLayout(LayoutKind.Explicit)]
        internal struct VersionDetailUnion
        {
            [FieldOffset(0)]
            internal uint Version;
            [FieldOffset(0)]
            internal byte VersionDetail_MajorVersion;
            [FieldOffset(1)]
            internal byte VersionDetail_MinorVersion;
            [FieldOffset(2)]
            internal byte VersionDetail_SubVersion;
            [FieldOffset(3)]
            internal byte VersionDetail_SubMinorVersion;
        }

        //	  union {
        //		GUID LogInstanceGuid;
        //		struct {
        //		  ULONG   StartBuffers;
        //		  ULONG   PointerSize;
        //		  ULONG   EventsLost;
        //		  ULONG   Reserved32;
        //		};
        //	  };
        [StructLayout(LayoutKind.Explicit)]
        internal struct BufferUnion
        {
            [FieldOffset(0)]
            internal Guid LogInstanceGuid;

            [FieldOffset(0)]
            internal uint StartBuffers;
            [FieldOffset(4)]
            internal uint PointerSize;
            [FieldOffset(8)]
            internal uint EventsLost;
            [FieldOffset(12)]
            internal uint Reserved32;
        }

        //	typedef struct _TIME_ZONE_INFORMATION { 
        //		LONG       Bias; 
        //		WCHAR      StandardName[ 32 ]; 
        //		SYSTEMTIME StandardDate; 
        //		LONG       StandardBias; 
        //		WCHAR      DaylightName[ 32 ]; 
        //		SYSTEMTIME DaylightDate; 
        //		LONG       DaylightBias; 
        //	} TIME_ZONE_INFORMATION, *PTIME_ZONE_INFORMATION; 

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct TimeZoneInformation
        {
            internal int Bias;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            internal string StandardName;
            internal SYSTEMTIME StandardDate;
            internal int StandardBias;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            internal string DaylightName;
            internal SYSTEMTIME DaylightDate;
            internal int DaylightBias;
        }

        //	typedef struct _SYSTEMTIME { 
        //		WORD wYear; 
        //		WORD wMonth; 
        //		WORD wDayOfWeek; 
        //		WORD wDay; 
        //		WORD wHour; 
        //		WORD wMinute; 
        //		WORD wSecond; 
        //		WORD wMilliseconds; 
        //	} SYSTEMTIME, *PSYSTEMTIME; 
        [StructLayout(LayoutKind.Sequential)]
        internal struct SYSTEMTIME
        {
            internal short wYear;
            internal short wMonth;
            internal short wDayOfWeek;
            internal short wDay;
            internal short wHour;
            internal short wMinute;
            internal short wSecond;
            internal short wMilliseconds;
        }

        #endregion

        #region EVENT_TRACE structure definition

        // struct _EVENT_TRACE {
        //    EVENT_TRACE_HEADER Header;
        //    ULONG InstanceId;
        //    ULONG ParentInstanceId;
        //    GUID ParentGuid;
        //    PVOID MofData;
        //    ULONG MofLength;
        //    ULONG ClientContext;
        // } EVENT_TRACE
        [StructLayout(LayoutKind.Sequential)]
        internal struct EVENT_TRACE
        {
            internal ushort Size;
            internal EventTraceFlags Flags;
            internal EventTraceType Type;
            internal byte Level;
            internal ushort Version;
            internal uint ThreadId;
            internal uint ProcessId;
            internal long TimeStamp;
            internal Guid Guid;
            internal TimeUnion TimeUnion;
            internal uint InstanceId;
            internal uint ParentInstanceId;
            internal Guid ParentGuid;
            internal IntPtr MofData;
            internal uint MofLength;
            internal uint ClientContext;
        }

        [StructLayout(LayoutKind.Explicit)]
        internal struct TimeUnion
        {
            [FieldOffset(0)]
            internal uint KernelTime;
            [FieldOffset(4)]
            internal uint UserTime;
            [FieldOffset(0)]
            internal ulong ProcessorTime;
        }

        #endregion
    }
}